﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using Me.Practice.ElevatorEngine.Configuration;
using Me.Practice.ElevatorEngine.Domain.Entity;

namespace Me.Practice.ElevatorEngine.Infrastructure
{
    public class BuildingContext
	{
        private static readonly object SSyncObect = new object();
	    private static volatile BuildingContext _sInstance;

		private readonly Building _building;
		private BuildingController _buildingController;
		
		private BuildingContext()
		{
            var floors = new List<Floor>();
            for (var i = 1; i <= ElevatorEngineConfiguration.ContextConfiguration.FloorCount; i++)
                floors.Add(new Floor(new Floor.Init { Number = i }));

		    var elevators =
		        (from ElevatorConfigurationElement elevatorConfiguration in
		             ElevatorEngineConfiguration.ContextConfiguration.Elevators
		         select new Elevator(new Elevator.Init
		                                 {
		                                     Id = elevatorConfiguration.Name,
		                                     Capacity = elevatorConfiguration.Capacity,
		                                     Timeout = elevatorConfiguration.Timeout,
		                                     Persons = new List<Person>(elevatorConfiguration.Capacity),
		                                     State = ElevatorState.Unpowered
		                                 })).ToList();

		    _building = new Building(new Building.Init
            {
                Name = ElevatorEngineConfiguration.ContextConfiguration.Name,
                Address = ElevatorEngineConfiguration.ContextConfiguration.Address,
                Floors = new ReadOnlyCollection<Floor>(floors),
                Elevators = elevators
            });	
		}

        private static BuildingContext Instance
	    {
	        get
	        {
	            if(_sInstance == null)
	            {
	                lock(SSyncObect)
	                {
	                    if(_sInstance == null)
	                    {
	                        _sInstance = new BuildingContext();
                            Instance._buildingController = new BuildingController(Instance._building);                            
	                    }
	                }
	            }
	            return _sInstance;
	        }
	    }

	    internal static BuildingController Controller
	    {
            get { return Instance._buildingController; }
	    }

		internal static IEnumerable<Floor> Floors 
		{
			get
			{
                return Instance._building.Floors;
			}
		}

        internal static IEnumerable<Elevator> Elevators
        {
            get
            {
                return Instance._building.Elevators;
            }
        }

        public static void ActivateEngine()
        {
            foreach (var fc in Instance._buildingController.FloorControllers)
            {
                var floorThread = new Thread(fc.Open)
		                     {Name = string.Format("Floor {0} thread", fc.ControlledFloor.Number)};
                
                floorThread.Start(); 
            }

            foreach (var ec in Instance._buildingController.ElevatorControllers)
            {
                var elevatorThread = 
                    new Thread(ec.ActivateElevator) { Name = string.Format("Elevator {0} thread", ec.ControlledElevator.Id) };
                elevatorThread.Start();
            }
        }

        public static void DeactivateEngine()
        {
        	foreach (var ec in Instance._buildingController.ElevatorControllers)
                ec.DeactivateElevator();   
        	
        	foreach (var fc in Instance._buildingController.FloorControllers)
                fc.Close();   
        }

        public static void ActivateElevator(string id)
		{
            Instance._buildingController.ActivateElevator(id);
		}

        public static void DeactivateElevator(string id)
		{
            Instance._buildingController.DeactivateElevator(id);
		}

        public static BuildingReport GetBuildingReport()
        {
            var floorReports = Floors.Select(floor => new FloorReport(new FloorReport.Init
                                                                          {
                                                                              FloorNumber = floor.Number,
                                                                              PersonCount = floor.Count
                                                                          })).ToList();

            var elevatorReports = Elevators.Select(elevator => new ElevatorReport(
                                                                   new ElevatorReport.Init
                                                                       {
                                                                           CurrentCapacity =
                                                                               elevator.Capacity,

                                                                           CurrentFloorNumber =
                                                                               elevator.CurrentFloorNumber,

                                                                           CurrentPersonCount =
                                                                               elevator.Persons.Count(),

                                                                           Id = elevator.Id,

                                                                           State = elevator.State

                                                                       })).ToList();

            var report = new BuildingReport(
                new BuildingReport.Init
                    {
                        FloorCount = Instance._building.Floors.Count(),
                        ElevtorCount = Instance._building.Elevators.Count(),
                        FloorReports = floorReports,
                        ElevatorReports = elevatorReports
                    }
                );

            return report;
        }

        public static ElevatorReport GetElevatorReport(string elevatorId)
		{
            var reportedElevator = Instance._building.Elevators.Single(e => e.Id == elevatorId);
			
			var report = new ElevatorReport(
				new ElevatorReport.Init {
                    Id = string.Format("{0} in thread:({1})", reportedElevator.Id, Thread.CurrentThread.Name),
					CurrentCapacity = reportedElevator.Capacity,
					CurrentFloorNumber = reportedElevator.CurrentFloorNumber,
					CurrentPersonCount = reportedElevator.Persons.Count(),
					State = reportedElevator.State
				}
			);
			
			return report;
		}		
	}
}
